from py2neo import Graph, Node, Relationship
from py2neo import NodeMatcher, RelationshipMatcher
from ..config.config import Config
from tqdm import tqdm
import json


class Neo4jGraph:
    def __init__(self):
        # 实例化config对象
        self.config = Config()
        self.url = "bolt://localhost:7687"
        # 用户名
        self.username = "neo4j"
        # 密码
        self.password = "jhwgjhwg"
        # 实例化数据库
        self.graph = Graph(self.url, auth=(self.username, self.password))
        # 实例化节点查询对象
        self.node_match = NodeMatcher(self.graph)
        # 实例化关系查询对象
        self.relation_match = RelationshipMatcher(self.graph)

    def connect_neo4j(self):
        try:
            # 链接neo4j数据库,输入地址，用户名，密码
            self.graph = Graph(self.url, auth=(self.username, self.password))
            # 节点查询对象
            self.node_match = NodeMatcher(self.graph)
            # 关系查询对象
            self.relation_match = RelationshipMatcher(self.graph)
            print("数据库neo4j连接成功")
        except Exception as e:
            print(f"数据库连接失败：{e}")


    def load_file_create_map(self):
        """
        将spo数据全部导入Neo4j数据库中

        """
        # 第一次运行可以清空所有数据，
        self.graph.delete_all()
        # 获取数据
        with open(f"{self.config.entertainment_data_path}\predict_spo.json","r",encoding="utf-8") as fr:
            for line in tqdm(fr.readlines()):
                line = json.loads(line)
                # print(line.keys())          # 看看到底有哪些字段
                # print(line)                 # 把整个字典打印出来
                # 定义主实体节点的标签
                subject_label = line["subject_type"]
                # 定义主实体节点属性
                sub_attrs = {"name":line["subject"]}
                # 创建节点
                self.create_node(subject_label,sub_attrs)

                # 定义客实体节点标签
                object_label = line["object_type"]

                # 定义主实体节点属性
                if "日期" in line["predicate"]:
                    obj_attrs = {"data":line["object"]}
                else:
                    obj_attrs = {"name":line["object"]}
                self.create_node(object_label,obj_attrs)
                # 定义主体和客体之间的关系
                r_name = line["predicate"]
                self.create_relationship(subject_label,sub_attrs,object_label,obj_attrs,r_name)
                # 4. 打印真实数量
                node_cnt = self.graph.run("MATCH (n) RETURN count(n) as c").data()[0]['c']
                rel_cnt  = self.graph.run("MATCH ()-->() RETURN count(*) as c").data()[0]['c']
                print(f">>> 上传完成！节点数：{node_cnt}，关系数：{rel_cnt}")


# # 创建节点
#     def create_node(self, label, attrs):
#         # 第一次：如果你的图数据库之前的数据是没有用的可以直接清空
#         # graph.delete_all()
#         # 构建节点的属性，如果一个节点有多个属性要同时判断，所以加上and
#         value = self.match_node(label, attrs)
#         # attrs = attrs.keys()[0] + "=" + attrs.values()[0]
#         key = list(attrs.keys())[0]
#         value = list(attrs.values())[0]
#         attrs = f"{key}={value}"
#         # breakpoint()
#         # pdb.set_trace()  # 在这里设置断点
#         # 如果要创建的节点不存在则再创建
#         if value is None:
#             node = Node(label, **attrs)
#             node = self.graph.create(node)
#             return node
#         return value
    def create_node(self, label, attrs: dict):
        # 1. 先查重
        node = self.node_match.match(label, **attrs).first()
        if node:                       # 已存在
            return node
        # 2. 创建新节点
        node = Node(label, **attrs)    # attrs 必须是字典
        self.graph.create(node)
        return node

    # 使用NodeMatcher查询节点
    def match_node(self, label, attrs):
        condition = ""
        for key, value in attrs.items():
            condition += '_.%s=' % key + '\"' + value + '\"' + " and "
        # 将condition最后的 and 字符去掉(注意and前后有两个空格)
        condition = condition[:-5]
        # 根据属性条件查询节点是否已经存在，若存在则返回该节点，否则返回None。
        # 注意加上.first()返回一个节点结果，不加.first()返回所有符合要求的节点结果
        value = self.node_match.match(label).where(condition).first()
        return value

    def create_relationship(self,label1,attrs1,label2,attrs2,r_name):
        # label1 第一个节点标签
        # attrs1 第一个节点属性
        # label2 第二个节点标签
        # attrs2 第二个节点属性
        # r_name 节点之间的关系
        # todo
        # r_name 关系名称
        # 创建两个节点
        value1 = self.match_node(label1,attrs1)
        value2 = self.match_node(label2,attrs2)
        # 判断实体是否都存在，否则无法创建关系
        if value1 is None or value2 is None:
            return False
        # 创建关系
        r = Relationship(value1,r_name,value2)
        self.graph.create(r)


    def get_all_spo(self):
        """
        获取数据库中所有的 spo 关系
        :return: list[dict]
        """
        query = """
        MATCH (s)-[p]->(o)
        RETURN s.name AS subject, type(p) AS predicate, o.name AS object
        LIMIT 1000
        """
        result = self.graph.run(query).data()
        return result

    



    
    


    